11.2 性能测试
性能测试函数以Benchmark为名称前缀,同样保存在“*_test.go”文件里。
func add(x,y int)int{ return x+y }
func BenchmarkAdd(b*testing.B) { for i:=0;i<b.N;i++ { _ =add(1,2) } }
输出:
$go test-bench.
BenchmarkAdd-4 2000000000 0.52 ns/op ok test 1.107s
测试工具默认不会执行性能测试,须使用bench参数。它通过逐步调整B.N值,反复执行测试函数,直到能获得准确的测量结果。
func BenchmarkAdd(b*testing.B) { println(“B.N=“,b.N)
for i:=0;i<b.N;i++ { _ =add(1,2) } }
输出:
$go test-bench.
BenchmarkAdd-4
B.N=1 B.N=100 B.N=10000 B.N=1000000 B.N=100000000 B.N=2000000000
2000000000 0.55 ns/op ok test 1.171s
如果希望仅执行性能测试,那么可以用run=NONE忽略所有单元测试用例。
默认就以并发方式执行测试,但可用cpu参数设定多个并发限制来观察结果。
$go test-bench. -cpu 1,2,4
BenchmarkAdd 2000000000 0.52 ns/op BenchmarkAdd-2 2000000000 0.51 ns/op BenchmarkAdd-4 2000000000 0.52 ns/op ok test 3.281s
某些耗时的目标,默认循环次数过少,取平均值不足以准确计量性能。可用benchtime设定最小测试时间来增加循环次数,以便返回更准确的结果。
func sleep() { time.Sleep(time.Second) }
func BenchmarkSleep(b*testing.B) { for i:=0;i<b.N;i++ { sleep() } }
输出:
$go test-bench. -benchtime 5s
BenchmarkAdd-4 2000000000 0.51 ns/op # 循环次数足够 BenchmarkSleep-4 5 1001927937 ns/op # 次数不足,延长执行时间 ok test 7.103s # 同样通过调整B.N重新测试
timer
如果在测试函数中要执行一些额外操作,那么应该临时阻止计时器工作。
func BenchmarkAdd(b*testing.B) { time.Sleep(time.Second) b.ResetTimer() // 重置
for i:=0;i<b.N;i++ { _ =add(1,2)
if i==1{
b.StopTimer() // 暂停
time.Sleep(time.Second)
b.StartTimer() // 恢复
}
}
}
memory
性能测试关心的不仅仅是执行时间,还包括在堆上的内存分配,因为内存分配和垃圾回收的相关操作也应计入消耗成本。
func heap() []byte{ return make([]byte,1024*10) }
func BenchmarkHeap(b*testing.B) { for i:=0;i<b.N;i++ { _ =heap() } }
输出:
$go test-bench. -benchmem-gcflags”-N-l” # 禁用内联和优化,以便观察结果
BenchmarkHeap-4 1000000 2392 ns/op 10496 B/op 1 allocs/op ok test 2.424s
输出结果包括单次执行堆内存分配总量和次数。
也可将测试函数设置为总是输出内存分配信息,无论使用benchmem参数与否。
func BenchmarkHeap(b*testing.B) { b.ReportAllocs() b.ResetTimer()
for i:=0;i<b.N;i++ { _ =heap() } }